/* Time-stamp: <2011-05-02 20:54:17 xinhaoyuan> */
/* This file is original from the assignment JOS */

#include <framework/framework_i.h>

#define CMDBUF_SIZE     80      // enough for one VGA text line

struct command_s {
     const char *name;
     const char *desc;
     // return -1 to force monitor to exit
     int (*func)(int argc, char** argv, struct trapframe_s* tf);
};

static int mon_help(int argc, char** argv, struct trapframe_s *tf);
static int mon_kerninfo(int argc, char** argv, struct trapframe_s *tf);
static int mon_backtrace(int argc, char** argv, struct trapframe_s *tf);
static int mon_mapping(int argc, char **argv, struct trapframe_s *tf);

static struct command_s commands[] = {
     { "help", "Display this list of commands", mon_help },
     { "kerninfo", "Display information about the kernel", mon_kerninfo },
     { "bt", "Display information of stack", mon_backtrace},
     { "backtrace", "Display information of stack", mon_backtrace},
	 { "mapping", "Display information about current linear address mapping", mon_mapping},
};

#define NCOMMANDS (sizeof(commands)/sizeof(commands[0]))

/***** Implementations of basic kernel monitor commands *****/

static int
mon_help(int argc, char **argv, struct trapframe_s *tf)
{
     int i;

     for (i = 0; i < NCOMMANDS; i++)
		  cprintf("%s - %s\n", commands[i].name, commands[i].desc);
     return 0;
}

static int
mon_kerninfo(int argc, char **argv, struct trapframe_s *tf)
{
     cprintf("Special kernel symbols:\n");
     cprintf("  _start     %08x (virt)  %08x (phys)\n", _start,    _start - KBASE);
     cprintf("  _text      %08x (virt)  %08x (phys)\n", _text,     _text - KBASE);
     cprintf("  _text_end  %08x (virt)  %08x (phys)\n", _text_end, _text_end - KBASE);
     cprintf("  _bss       %08x (virt)  %08x (phys)\n", _bss,      _bss - KBASE);
     cprintf("  _end       %08x (virt)  %08x (phys)\n", _end,      _end - KBASE);
     cprintf("Kernel executable memory footprint: %dKB\n",
			 (_end - _text + 1023)/1024);
     return 0;
}

static int
mon_backtrace(int argc, char **argv, struct trapframe_s *tf)
{
     uint32_t ebp, nebp;
     uint32_t eip = 0;
     struct eipdebuginfo_s debug_info;

     asm volatile("movl %%ebp, %0" : "=r" (ebp));

     cprintf("Stack backtrace:\n");
     /* cprintf("  ebp: %p\n", ebp); */

	 uint32_t lines = 0;
     while (1)
     {
		  eip = *((uint32_t *)(ebp) + 1);
		  nebp = *(uint32_t *)(ebp);

		  ebp = nebp;

		  /* break if the ptr down to user mode */
		  if (ebp < KBASE)
		  {
			   if (tf == NULL)
					break;
			   if ((ebp = tf->regs.ebp) < KBASE)
					break;
			   tf = NULL;
			   cprintf("============================================================\n");
		  }
          
		  cprintf("  ebp: %p  eip: %p", ebp, eip);
          
		  if (debuginfo_eip(eip, &debug_info) >= 0)
		  {
			   cprintf("  args[%d]", debug_info.eip_fn_narg);
			   int arg_idx;
			   uint32_t arg;
			   for (arg_idx = 0; arg_idx < debug_info.eip_fn_narg; ++ arg_idx)
			   {
					arg = *((uint32_t *)ebp + arg_idx + 2);
					cprintf(" %08x", arg);
			   }

			   cprintf("\n\t%s:%d: ", debug_info.eip_file, debug_info.eip_line, debug_info.eip_fn_narg);
               
			   int i;
			   for (i = 0; i != debug_info.eip_fn_namelen; ++i)
					cputchar(debug_info.eip_fn_name[i]);

			   cprintf("+%d\n", eip - debug_info.eip_fn_addr);
		  }
		  else
		  {
			   // cprintf("%s : %d\n", debug_info.eip_file, debug_info.eip_line);
			   cprintf(" [debug info missing]\n");
		  }

		  if (++ lines == 10)
		  {
			   lines = 0;
			   cprintf("Enter to show next 10 items\n");
			   readline(NULL);
		  }
     }


	 cprintf("------------------------------------------------------------\n");
	 cprintf("Trapframe(%08x):\n", tf);
	 if (tf != NULL) cprintf("EIP: %08x\n", tf->hw.eip);
	 cprintf("============================================================\n");
     
     return 0;
}

static int
mon_mapping(int argc, char **argv, struct trapframe_s *tf)
{
#if 0
	 if (argc == 1)
	 {
		  cprintf("usage: %s [addr1] [addr2] ...", argv[0]);
	 }
	 else
	 {
		  int i;
		  uintptr_t addr;
                  
		  for (i = 1; i < argc; ++ i)
		  {
			   char *str = argv[i];
			   if (parse_uint32(&str, &addr) != 0)
			   {
					cprintf("%s is not a vaild address\n", argv[i]);
			   }
			   uint32_t pte = 0; // get_vpt((void *)addr);
			   cprintf("vpt (%08x) = %08x\n", addr, pte);
		  }
                           
	 }
#else
	 cprintf("Disabled\n");
#endif
         
	 return 0;
}


/***** Kernel monitor command interpreter *****/

#define WHITESPACE "\t\r\n "
#define MAXARGS 16

static int
runcmd(char *buf, struct trapframe_s *tf)
{
     int argc;
     char *argv[MAXARGS];
     int i;

     // Parse the command buffer into whitespace-separated arguments
     argc = 0;
     argv[argc] = 0;
     while (1) {
		  // gobble whitespace
		  while (*buf && strchr(WHITESPACE, *buf))
			   *buf++ = 0;
		  if (*buf == 0)
			   break;

		  // save and scan past next arg
		  if (argc == MAXARGS-1) {
			   cprintf("Too many arguments (max %d)\n", MAXARGS);
			   return 0;
		  }
		  argv[argc++] = buf;
		  while (*buf && !strchr(WHITESPACE, *buf))
			   buf++;
     }
     argv[argc] = 0;

     // Lookup and invoke the command
     if (argc == 0)
		  return 0;
     for (i = 0; i < NCOMMANDS; i++) {
		  if (strcmp(argv[0], commands[i].name) == 0)
			   return commands[i].func(argc, argv, tf);
     }
     cprintf("Unknown command '%s'\n", argv[0]);
     return 0;
}

void
monitor(struct trapframe_s *tf)
{
     char *buf;

     cprintf("Here is the %s monitor\n", __NAME);
     cprintf("Type 'help' for a list of commands.\n");

     while (1) {
		  buf = readline("K> ");
          
		  if (strcmp(buf, "quit") == 0) break;
          
		  if (buf != NULL)
			   if (runcmd(buf, tf) < 0)
			   {
					break;
			   }
     }

     breakpoint();
}
